package com.stars.easyms.logger.support.logback.handler;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import com.stars.easyms.logger.function.EasyMsLoggerExecutor;
import com.stars.easyms.logger.config.EasyMsLoggerHelper;
import com.stars.easyms.logger.support.EasyMsOverride;
import com.stars.easyms.logger.support.logback.compatibility.CowArrayListCompatibility;
import com.stars.easyms.logger.support.logback.util.EasyMsAsynAppenderAddExecutorHolder;
import com.stars.easyms.logger.support.logback.util.EasyMsLoggerReflectUtil;
import com.stars.easyms.logger.util.EasyMsLoggerExecutorHolder;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * <p>className: EasyMsAppenderAttachableImpl</p>
 * <p>description: 重写AppenderAttachableImpl类</p>
 *
 * @author guoguifang
 * @date 2019-08-23 16:34
 * @since 1.3.0
 */
public final class EasyMsAppenderAttachableImpl extends AppenderAttachableImpl<ILoggingEvent> implements EasyMsOverride {

    private final List<Appender<ILoggingEvent>> appenderList;

    public EasyMsAppenderAttachableImpl(AppenderAttachableImpl sourceAai) {
        this.appenderList = EasyMsLoggerReflectUtil.getAaiAppenderList(sourceAai);
    }

    @Override
    public void addAppender(Appender<ILoggingEvent> newAppender) {
        if (newAppender == null) {
            throw new IllegalArgumentException("Null argument disallowed");
        }
        CowArrayListCompatibility.addIfAbsent(appenderList, newAppender);
    }

    @Override
    public int appendLoopOnAppenders(ILoggingEvent event) {
        int size = 0;
        if (EasyMsLoggerHelper.isLogAsynEnabled()) {
            final List<EasyMsAsynAppenderAddExecutor> easyMsAsynAppenderAddExecutors = new ArrayList<>();
            EasyMsAsynAppenderAddExecutorHolder.setAsynAppenderAddExecutorList(easyMsAsynAppenderAddExecutors);
            try {
                for (Appender<ILoggingEvent> appender : CowArrayListCompatibility.asTypedArray(appenderList)) {
                    size++;
                    if (appender instanceof OutputStreamAppender) {
                        easyMsAsynAppenderAddExecutors.add(EasyMsAsynAppenderEncoderCache.getAddExecutor((OutputStreamAppender) appender, event));
                    } else {
                        appender.doAppend(event);
                    }
                }
            } finally {
                EasyMsAsynAppenderAddExecutorHolder.clear();
            }
            List<EasyMsLoggerExecutor> easyMsLoggerExecutorList = EasyMsLoggerExecutorHolder.getLoggerExecutorList();
            if (easyMsLoggerExecutorList != null) {
                easyMsLoggerExecutorList.add(() -> easyMsAsynAppenderAddExecutors.forEach(EasyMsAsynAppenderAddExecutor::execute));
            } else {
                easyMsAsynAppenderAddExecutors.forEach(EasyMsAsynAppenderAddExecutor::execute);
            }
        } else {
            for (Appender<ILoggingEvent> appender : CowArrayListCompatibility.asTypedArray(appenderList)) {
                appender.doAppend(event);
                size++;
            }
        }
        return size;
    }

    @Override
    public Iterator<Appender<ILoggingEvent>> iteratorForAppenders() {
        return appenderList.iterator();
    }

    @Override
    public Appender<ILoggingEvent> getAppender(String name) {
        if (name == null) {
            return null;
        }
        for (Appender<ILoggingEvent> appender : appenderList) {
            if (name.equals(appender.getName())) {
                return appender;
            }
        }
        return null;
    }

    @Override
    public boolean isAttached(Appender<ILoggingEvent> appender) {
        if (appender == null) {
            return false;
        }
        for (Appender<ILoggingEvent> a : appenderList) {
            if (a == appender) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void detachAndStopAllAppenders() {
        for (Appender<ILoggingEvent> a : appenderList) {
            a.stop();
        }
        appenderList.clear();
    }

    @Override
    public boolean detachAppender(Appender<ILoggingEvent> appender) {
        if (appender == null) {
            return false;
        }
        boolean result;
        result = appenderList.remove(appender);
        return result;
    }

    @Override
    public boolean detachAppender(String name) {
        if (name == null) {
            return false;
        }
        boolean removed = false;
        for (Appender<ILoggingEvent> a : appenderList) {
            if (name.equals((a).getName())) {
                removed = appenderList.remove(a);
                break;
            }
        }
        return removed;
    }
}